package com.laohei.bili_tube.app

import android.util.Log
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.activity.compose.LocalActivity
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material.icons.outlined.Subscriptions
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldDefaults
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldLayout
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import com.laohei.bili_sdk.user.GetUserInfo
import com.laohei.bili_tube.R
import com.laohei.bili_tube.app.component.SideNavigateRail
import com.laohei.bili_tube.component.appbar.BottomAppBarItem
import com.laohei.bili_tube.component.appbar.SmallBottomAppBar
import com.laohei.bili_tube.core.COOKIE_KEY
import com.laohei.bili_tube.core.FACE_URL_KEY
import com.laohei.bili_tube.core.UP_MID_KEY
import com.laohei.bili_tube.core.USERNAME_KEY
import com.laohei.bili_tube.core.VIP_STATUS_KEY
import com.laohei.bili_tube.core.correspondence.Event
import com.laohei.bili_tube.core.correspondence.EventBus
import com.laohei.bili_tube.core.util.setValue
import com.laohei.bili_tube.core.util.useLightSystemBarIcon
import com.laohei.bili_tube.dataStore
import com.laohei.bili_tube.presentation.dynamic.DynamicScreen
import com.laohei.bili_tube.presentation.history.HistoryScreen
import com.laohei.bili_tube.presentation.home.HomeScreen
import com.laohei.bili_tube.presentation.login.QRCodeLoginScreen
import com.laohei.bili_tube.presentation.mine.MineScreen
import com.laohei.bili_tube.presentation.player.PlayerScreen
import com.laohei.bili_tube.presentation.playlist.PlaylistScreen
import com.laohei.bili_tube.presentation.splash.SplashScreen
import com.laohei.bili_tube.presentation.subscription.SubscriptionScreen
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.compose.koinInject

private const val TAG = "App"

@Composable
fun App() {
    //确切来讲 compose 只会有一个 activity
    val activity = LocalActivity.current
    // 多个上下文信息
    val context = LocalContext.current
    //创建一个导航,用来管理应用内的导航
    val navController = rememberNavController()
    //每次导航目的地的变化都会触发该状态的变化
    val currentDestination by navController.currentBackStackEntryAsState()
    //是否登录
    var isLogin by rememberSaveable(Unit) { mutableStateOf(false) }
    //是否是播放路由
    val isPlayRoute = currentDestination?.destination?.hasRoute<Route.Play>() == true
    //不是播放路由
    if (isPlayRoute.not()) {
        //根据系统主题的变化更改导航栏和状态栏的颜色,如果是暗色主题那么状态栏 & 导航栏图标变白色,反之亦然
        activity?.useLightSystemBarIcon(isSystemInDarkTheme().not())
    }
    //注册事件监听器
    AppEventListener()

    LaunchedEffect(isLogin) {
        //登录状态变化会进来
        if (isLogin.not()) {
            return@LaunchedEffect
        }
        //是否为登录页面
        val isLoginRoute = currentDestination?.destination?.hasRoute<Route.Login>() == true
        //不是登录页面则返回不处理
        if (isLoginRoute.not()) {
            return@LaunchedEffect
        }
        //在执行lambda中的页面清除后将HomeGraph压入栈顶
        navController.navigate(Route.HomeGraph) {
            if (isLoginRoute) {
                //是登录界面清除到登录页面,包括登录页面,如果登录页面前面有其他页面会跟着一起清楚
                popUpTo<Route.Login> { inclusive = true }
            } else {
                //不是登录界面清除到第一屏幕,包括第一屏幕,如果登录页面前面有其他页面会跟着一起清楚
                popUpTo<Route.Splash> { inclusive = true }
            }
            //如果目标目的地（HomeGraph）已经在栈顶，则不会创建新的实例，而是复用已有的实例，防止重复创建页面
            launchSingleTop = true
        }
    }

    LaunchedEffect(Unit) {
        //在丢失了cookie后将登陆状态变为未登陆
        context.dataStore.data.map { preferences ->
            !preferences[COOKIE_KEY].isNullOrBlank()
        }.collect {
            isLogin = it
        }
    }
    //获取用户配置
    GetAndCacheUserProfile(isLogin)
    //在app执行的处理
    ExitAppHandle()

    NavHost(
        navController = navController,
        //其实页面为第一屏幕
        startDestination = Route.Splash,
        //进入新页面时的动画从右向左（正方向)
        enterTransition = {
            slideInHorizontally { it }
        },
        //返回上一个页面时，当前页面的退出动画 从左向右（正方向）
        popExitTransition = {
            slideOutHorizontally { it }
        },
        //返回上一个页面时，上一个页面的进入动画 从左向右（反方向）
        popEnterTransition = {
            slideInHorizontally { -it }
        },
        // 离开当前页面时的动画 从右向左（反方向）
        exitTransition = {
            slideOutHorizontally { -it }
        }
    ) {
        composable<Route.Splash>(
            enterTransition = { fadeIn() },
            exitTransition = { fadeOut() }
        ) {
            SplashScreen {
                val startRoute = if (isLogin) Route.HomeGraph else Route.Login
                navController.navigate(startRoute) {
                    popUpTo<Route.Splash> { inclusive = true }
                    launchSingleTop = true
                }
            }
        }
        composable<Route.Login> { QRCodeLoginScreen() }
        composable<Route.Play> {
            PlayerScreen(
                params = it.toRoute(),
                upPress = { navController.navigateUp() }
            )
        }
        composable<Route.Playlist> { PlaylistScreen() }
        composable<Route.History> {
            HistoryScreen(
                navigateToRoute = { navController.navigate(it) }
            )
        }
        composable<Route.HomeGraph> { MainChildGraph(navController) }
    }
}

@Composable
private fun MainChildGraph(navController: NavController) {
    //获得一个协程作用域，用于在 Compose 中启动协程任务，适合处理点击事件后的异步操作。
    val scope = rememberCoroutineScope()
    //获取当前的 Android 上下文，用于访问资源（如字符串资源）
    val context = LocalContext.current
    //使用 rememberSaveable 保持选中的索引状态，即使重组或配置变化后，状态依然保留。
    var bottomAppBarSelectedIndex by rememberSaveable { mutableIntStateOf(0) }
    //构造底部导航项目：通过 Icons.Outlined.* 提供图标和利用 context.getString() 加载字符串资源，为底部导航栏的每一项创建一个 BottomAppBarItem。
    //使用 remember：列表只在第一次组合时构建一次，避免重复计算。
    val bottomAppBarItems = remember {
        listOf(
            BottomAppBarItem(
                icon = Icons.Outlined.Home,
                label = context.getString(R.string.str_home)
            ),
            BottomAppBarItem(
                icon = Icons.Outlined.Subscriptions,
                label = context.getString(R.string.str_scrscription)
            ),
            BottomAppBarItem(
                icon = Icons.Outlined.Person,
                label = context.getString(R.string.str_mine)
            )
        )
    }
    //创建一个独立的导航控制器，这样可以在当前页面区域内管理子页面的导航
    val mainChildNavController = rememberNavController()
    //实时获取当前展示的页面（路由）
    val currentDestination by mainChildNavController.currentBackStackEntryAsState()
    //自适应信息计算：根据当前窗口的适配信息（如屏幕尺寸、方向等），判断使用哪种导航布局类型，比如侧边栏（NavigationRail/NavigationDrawer）或底部导航栏（NavigationBar/None）
    val navSuiteType =
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(currentWindowAdaptiveInfo())

    //底部导航点击事件处理逻辑
    fun handleBottomEvent(index: Int) {
        //根据点击的索引选择对应的目标路由（主页、动态页或个人页）
        val route = when (index) {
            0 -> Route.HomeGraph.Home
            1 -> Route.HomeGraph.Dynamic
            else -> Route.HomeGraph.Mine
        }
        //是否处于主页
        val isHomeRoute =
            index == 0 && currentDestination?.destination?.hasRoute<Route.HomeGraph.Home>() == true
        //是否处于动态页面
        val isDynamicRoute =
            index == 1 && currentDestination?.destination?.hasRoute<Route.HomeGraph.Dynamic>() == true

        when {
            //如果当前已经在主页或者动态页面
            isHomeRoute || isDynamicRoute -> {
                scope.launch {
                    //发送一个刷新事件
                    EventBus.send(Event.NotificationChildRefresh)
                }
            }
            //不在则导航
            else -> {
                mainChildNavController.navigate(route) {
                    //此函数用于在执行新的导航操作前，弹出返回栈（Back Stack）中的特定目的地及其以上的所有目的地。这里指定了 Route.HomeGraph.Home，意味着在导航到新页面前，会移除返回栈中位于 Home 之上的所有页面，确保返回栈的整洁
                    popUpTo(Route.HomeGraph.Home) {
                        //此参数指示在弹出这些目的地时，保存它们的状态。这样，当用户通过导航返回这些页面时，这些页面可以恢复到之前的状态，而不是重新创建，提升了用户体验。
                        saveState = true
                    }
                    //此参数确保如果目标目的地当前位于返回栈的顶部，则不会创建该目的地的新实例，而是重用现有实例。这样可以避免在导航到同一页面时创建多个实例，节省资源并防止界面堆叠问题。
                    launchSingleTop = true
                    //当与 saveState = true 配合使用时，此参数指示在导航到先前保存过状态的目的地时，恢复其之前的状态。这意味着如果用户之前访问过该页面，并且状态已保存，则在返回该页面时，状态将被恢复，而不是重新初始化
                    restoreState = true
                }
            }
        }
    }
    //此组件是一个自适应的容器，它根据 navSuiteType 的不同来选择使用侧边导航栏还是底部导航栏。
    NavigationSuiteScaffoldLayout(
        navigationSuite = {
            when (navSuiteType) {
                //当 navSuiteType 为 NavigationDrawer 或 NavigationRail 时，使用 SideNavigateRail 显示侧边栏样式的导航组件
                NavigationSuiteType.NavigationDrawer,
                NavigationSuiteType.NavigationRail -> {
                    SideNavigateRail(
                        items = bottomAppBarItems,
                        selectedIndex = bottomAppBarSelectedIndex
                    ) { index ->
                        bottomAppBarSelectedIndex = index
                        handleBottomEvent(index)
                    }
                }
                //当 navSuiteType 为 None 或 NavigationBar 时，在 Surface 中使用 SmallBottomAppBar 展示底部导航栏，同时利用 navigationBarsPadding() 为底部系统栏留出空间。
                NavigationSuiteType.None,
                NavigationSuiteType.NavigationBar -> {
                    Surface(
                        modifier = Modifier
                            .background(MaterialTheme.colorScheme.background)
                            .navigationBarsPadding()
                    ) {
                        SmallBottomAppBar(
                            items = bottomAppBarItems,
                            selectedIndex = bottomAppBarSelectedIndex
                        ) { index ->
                            bottomAppBarSelectedIndex = index
                            handleBottomEvent(index)
                        }
                    }
                }
            }
        }
    ) {
        NavHost(
            navController = mainChildNavController,
            startDestination = Route.HomeGraph.Home,
        ) {
            homeGraph(navController)
        }
    }
}

@Composable
private fun ExitAppHandle() {
    val scope = rememberCoroutineScope()
    val context = LocalContext.current
    val activity = LocalActivity.current
    var backPressedTime by remember { mutableLongStateOf(0L) }
    //在没有东西可以返回的时候将会触发
    BackHandler(enabled = true) {
        val currentTime = System.currentTimeMillis()
        if (currentTime - backPressedTime < 2000) {
            // 退出应用
            activity?.finish()
        } else {
            // 提示用户再按一次退出
            backPressedTime = currentTime
            scope.launch {
                EventBus.send(Event.AppEvent.ToastEvent(context.getString(R.string.str_exit_app_hint)))
            }
        }
    }
}

@Composable
private fun AppEventListener() {
    val context = LocalContext.current
    LaunchedEffect(Unit) {
        //监听事件
        EventBus.events.collect { event ->
            //不是app的事件不做处理
            if ((event is Event.AppEvent).not()) {
                return@collect
            }
            when (val appEvent = event as Event.AppEvent) {
                is Event.AppEvent.ToastEvent -> {
                    //调用消息框
                    Toast.makeText(context, appEvent.message, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}

@Composable
private fun GetAndCacheUserProfile(isLogin: Boolean) {
    val context = LocalContext.current
    //存储在koin容器中的单例Bean
    val userInfo = koinInject<GetUserInfo>()

    LaunchedEffect(isLogin) {
        context.dataStore.data.firstOrNull()?.get(COOKIE_KEY)
            ?.apply {
                userInfo.getUserProfile(
                    cookie = this
                )?.data?.let {
                    Log.d(TAG, "App: $it")
                    context.setValue(UP_MID_KEY.name, it.mid)
                    context.setValue(FACE_URL_KEY.name, it.face)
                    context.setValue(USERNAME_KEY.name, it.uname)
                    context.setValue(VIP_STATUS_KEY.name, it.vipStatus)
                }
            }
    }
}


private fun NavGraphBuilder.homeGraph(navController: NavController) {
    composable<Route.HomeGraph.Home> {
        HomeScreen(
            navigateToRoute = {
                navController.navigate(it)
            }
        )
    }
    composable<Route.HomeGraph.Mine> { MineScreen(navigateToRoute = { navController.navigate(it) }) }
    composable<Route.HomeGraph.Subscription> { SubscriptionScreen() }
    composable<Route.HomeGraph.Dynamic> {
        DynamicScreen(
            navigateToRoute = {
                navController.navigate(it)
            }
        )
    }
}